﻿// Copyright (c) 2012 - 2021 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2021-03-19 version 4.0.0 Jared Zheng <jaredz at outlook dot com>

#ifndef __SERVER_CONFIG_H__
#define __SERVER_CONFIG_H__

#pragma once

#include "filelog.h"
#include "filepath.h"
#include "keyvalue.h"
#include "markup.h"
#include "CommonDefine.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 通用配置
class NOVTABLE CServerConfig : public MObject
{
public:
    enum LOAD_ID_ENUM
    {
        LOADI_RADIX       = 100,
    };

    enum LISTEN_PORT_ENUM
    {
        LISTENP_DEFAULT   = 9100, // 默认监听端口
    };

    enum TIME_DEFAULT_ENUM
    {
        TIMED_UPDATE_ID   = 1000,
        TIMED_UPDATE_MIN  = 1,       // [S]
        TIMED_UPDATE_DEF  = 5,       // [S]
        TIMED_WAIT_UNLINK = 500,     // [MS]
    };

    static CPCXStr ServerConfig;    // 配置
    static CPCXStr NetworkPtr;      // 网络

    static CPCXStr ConfigName;      // 默认配置文件名称
    static CPCXStr ConfigServer;    // 命令行配置名称

    static CPCXStr NetworkConfig;   // NetworkConfig
    static CPCXStr NetworkAttr;     // NetworkAttr=0(0表示使用默认值, 后面的都忽略)
    static CPCXStr NetworkThread;   // NetworkThread=0(cores)
    static CPCXStr NetworkAck;      // NetworkAck(连接完成握手包时间)
    static CPCXStr NetworkTimeout;  // NetworkTimeout(心跳包时间间隔)
    static CPCXStr NetworkBuffer;   // NetworkBuffer
    static CPCXStr NetworkJumbo;    // NetworkJumbo

    static CPCXStr CommonConfig;    // CommonConfig
    static CPCXStr FrameLogLevel;   // 框架模块日志等级
    static CPCXStr MgrLogLevel;     // 管理日志等级
    static CPCXStr UpdateTick;      // UpdateTick=30(秒)
    static CPCXStr PingEnable;      // 外网ping值检测
    static CPCXStr AnyListen;       // 是否支持所有地址都监听模式
    static CPCXStr FramePath;       // 模块路径
    static CPCXStr LoadServers;     // 加载的模块种类 : LoadServers=MasterServer|SlaveServer|NodeServer

    static CPCXStr MasterServer;    // master模块
    static CPCXStr SlaveServer;     // slave模块
    static CPCXStr NodeServer;      // node模块

    static CPCXStr Frame;
    static CPCXStr Rank;

    static CPCXStr Value;
    static CPCXStr Name;
    static CPCXStr Sync;
    static CPCXStr Ip;
    static CPCXStr Port;
    static CPCXStr Id;
    static CPCXStr Queue;
    static CPCXStr Load;
    static CPCXStr Rate;

    static CPCXStr Listen;
    static CPCXStr ListenIp;
    static CPCXStr ListenPort;
    static CPCXStr Connect;
    static CPCXStr Routine;
    static CPCXStr Inst;
    static CPCXStr Module;

    static CPCXStr LocalHost;
    static CPCXStr AddrAnyV4;       // ipv4 addr-any
    static CPCXStr AddrAnyV6;       // ipv6 addr-any
    static CPCXStr RoutineFolder;   // routine配置目录
    static CPCXStr LogFolder;       // log日志目录
    static CPCXStr LibPrefix;       // 模块文件前缀
    static CPCXStr LibExt;          // 模块文件后缀
    static XChar   PathSepChar;     // 路径分割符号

    typedef CTMap<CString, CString>         MAP_ARGV, *PMAP_ARGV;
    typedef CTMap<CString, CString>::PAIR   PAIR_ARGV;
public:
    CServerConfig(void);
    ~CServerConfig(void);
    // 获取文件管理器
    CFilePath& GetFilePath(void);
    // 获取key-value管理器
    CKeyValue& GetKeyValue(void);

    // -ServerConfig:test.txt
    //----------------------------------
    // -NetworkAttr:32(ipv4)/64(ipv6), ATTR_NETORDER不支持
    // -NetworkThread:1
    // -NetworkAck:8000
    // -NetworkTimeout:80000
    // -NetworkBuffer:8000
    // -NetworkJumbo:64000
    //----------------------------------
    // -FrameLogLevel
    // -MgrLogLevel
    // -UpdateTick
    // -PingEnable:
    // -AnyListen:
    // -FramePath:
    // -Routine:
    // -Log:
    // -LoadServers:
    //----------------------------------
    // -ConfigServer0---N:COMMON_TYPE_ENUM(开启每个编号的命令行配置)
    // -Name0---N:
    // -Sync0---N:
    // -Id0---N:
    // -Queue0---N:
    // -Load0---N:
    // -Rate0---N:
    // -Listen0---N:Port,Ip
    // -Connect0---N:3,ListenPort,ListenIp[,Port,Ip][;5,ListenPort,ListenIp,Port,Ip];
    // -Routine0---N:
    // -Inst0---N:
    // -Module0---N:
    void   SetArgs(PCXStr pszCmd);
    void   SetArgs(Int nArgc, PXStr* ppszArgv);

    // 初始化
    bool   Init(void);
    // 重新从配置文件加载
    bool   Update(void);
    // 退出
    void   Exit(void);
    // 打印配置信息到文件日志
    void   Dump(CFileLogPipe& Log, UInt uRank);

    // 获取网络层配置参数-属性
    Int    GetNetworkAttr(void) const;
    // 获取网络层配置参数-线程数量
    Int    GetNetworkThread(void) const;
    // 获取网络层配置参数-ACK时间
    Int    GetNetworkAck(void) const;
    // 获取网络层配置参数-超时时间
    Int    GetNetworkTimeout(void) const;
    // 获取网络层配置参数-普通接收或者发送缓存大小
    Int    GetNetworkBuffer(void) const;
    // 获取网络层配置参数-巨帧接收或者发送缓存大小
    Int    GetNetworkJumbo(void) const;
    // 获取框架模块日志等级
    Int    GetFrameLogLevel(void) const;
    // 获取管理日志等级
    Int    GetMgrLogLevel(void) const;
    // 获取同步数据间隔
    Int    GetUpdateTick(void) const;
    // 
    Int    GetTimeoutStep(void) const;
    // 外网ping值检测, TRUE表示支持
    Int    GetPingEnable(void) const;
    // 是否支持所有地址都监听模式, TRUE表示支持
    Int    GetAnyListen(void) const;
    // 获取加载服务模块情况
    Int    GetLoadServers(void) const;

    // 获取各服务的名称
    PCXStr GetServerName(UInt uRank) const;
    // 获取各服务的类型
    UShort GetServerType(UInt uRank) const;
    // 获取各服务的主同步层级编号
    UShort GetServerSync(UInt uRank) const;
    // 获取各服务的Id
    UShort GetServerId(UInt uRank) const;
    // 获取各服务的队列线程数量
    UShort GetServerQueue(UInt uRank) const;
    // 获取各服务的初始负载
    UShort GetServerLoad(UInt uRank) const;
    // 获取各服务的变化比率
    UShort GetServerRate(UInt uRank) const;
    // 获取各服务的监听地址信息
    bool   GetServerListen(UInt uRank, CStrAddr& strAddr, bool bAny2Local = false) const;
    // 获取各服务的连接绑定本地地址
    bool   GetServerConnect(UInt uRank, CStrAddr& strAddr, UInt uConnectRank = COMMONR_MASTER) const;
    // 获取各服务的业务配置
    bool   GetServerRoutine(UInt uRank, CString& strRoutine, CString& strInst, CString& strModule) const;

    const  SERVER_CONFIG& GetServerConfig(UInt uRank) const;

    PCXStr GetFramePath(void) const;
    PCXStr GetRoutinePath(void) const;
    PCXStr GetLogPath(void) const;

    void   CheckModule(CString& strModule) const;
private:
    bool LoadConfig(CXMLNode* pRoot);
    bool LoadNetwork(CXMLNode* pRoot);
    bool LoadCommon(CXMLNode* pRoot);
    bool LoadServer(CXMLNode* pRoot);
    bool LoadRank(UInt uRank, CXMLNode* pRank);

    void CheckConfig(void);
    void CheckNetwork(void);
    void CheckCommon(void);
    void CheckServer(void);
    void CheckPath(void);

    void CheckAttr(UInt uRank);
    void CheckAddr(UInt uRank);
    void CheckRoutine(UInt uRank);
    
    void UpdateNetwork(void);
    void UpdateCommon(void);
    void UpdateServer(void);
    void UpdateServer(UInt uRank, bool bArgv = false);

    void DumpNetwork(CFileLogPipe& Log);
    void DumpCommon(CFileLogPipe& Log);
    void DumpServer(CFileLogPipe& Log, UInt uRank, bool bListenOnly = false);

    bool LoadServerRank(CXMLNode* pNode, UInt& uRank);
    
    bool LoadServerAttr(CXMLNode* pNode, SERVER_CONFIG& SrvConfig);
    bool LoadServerAttr(UInt uRank, SERVER_CONFIG& SrvConfig);
    bool LoadServerAddr(CXMLNode* pNode, SERVER_CONFIG& SrvConfig);
    bool LoadServerAddr(UInt uRank, SERVER_CONFIG& SrvConfig);
    bool LoadServerRoutine(CXMLNode* pNode, SERVER_CONFIG& SrvConfig);
    bool LoadServerRoutine(UInt uRank, SERVER_CONFIG& SrvConfig);

    bool LoadValue(CXMLNode* pNode, PCXStr pszAttr, Int& nValue);
    bool LoadValue(CXMLNode* pNode, PCXStr pszElement, PCXStr pszAttr, Int& nValue);
    bool LoadValue(CXMLNode* pNode, PCXStr pszAttr, UShort& usValue);
    bool LoadValue(CXMLNode* pNode, PCXStr pszElement, PCXStr pszAttr, UShort& usValue);
    bool LoadValue(CXMLNode* pNode, PCXStr pszAttr, CString& strValue);
    bool LoadValue(CXMLNode* pNode, PCXStr pszElement, PCXStr pszAttr, CString& strValue);
public:
    // network
    Int         m_nNetworkAttr;    // 网络层配置参数-属性
    Int         m_nNetworkThread;  // 网络层配置参数-线程数量
    Int         m_nNetworkAck;     // 网络层配置参数-握手时间
    Int         m_nNetworkTimeout; // 网络层配置参数-超时时间
    Int         m_nNetworkBuffer;  // 网络层配置参数-普通接收或者发送缓存大小
    Int         m_nNetworkJumbo;   // 网络层配置参数-巨帧接收或者发送缓存大小
    // servers
    Int         m_nFrameLogLevel;  // 框架模块日志等级
    Int         m_nMgrLogLevel;    // 管理日志等级
    Int         m_nUpdateTick;     // 同步服务数据间隔
    Int         m_nPingEnable;     // 外网ping值检测
    Int         m_nAnyListen;      // 是否支持所有地址都监听模式
    Int         m_nLoadServers;    // 加载服务注册模块情况

    CString     m_strConfigName;   // 配置文件路径
    CString     m_strFramePath;    // 框架模块路径
    CString     m_strRoutinePath;  // routine配置路径
    CString     m_strLogPath;      // log文件路径

    CFilePath       m_FilePath;    // 文件管理
    CKeyValue       m_KeyValue;    // key-value管理

    MAP_ARGV        m_cmdArgv;     // 命令行参数

    SERVER_CONFIG   m_SrvConfig[COMMONR_MAX];
};

INLINE CServerConfig::CServerConfig(void)
: m_nNetworkAttr(0)
, m_nNetworkThread(0)
, m_nNetworkAck(0)
, m_nNetworkTimeout(0)
, m_nNetworkBuffer(0)
, m_nNetworkJumbo(0)
#ifdef __RUNTIME_DEBUG__
, m_nFrameLogLevel(LOGL_ALL|LOGL_DEVPRINT)
, m_nMgrLogLevel(LOGL_ALL|LOGL_DEVPRINT)
#else
, m_nFrameLogLevel(LOGL_ALL)
, m_nMgrLogLevel(LOGL_ALL)
#endif
, m_nUpdateTick(TIMED_UPDATE_DEF)
, m_nPingEnable(FALSE)
, m_nAnyListen(TRUE)
, m_nLoadServers(COMMONT_ALL)
, m_strConfigName(ConfigName)
{
    for (UInt i = COMMONR_MASTER; i < COMMONR_MAX; ++i)
    {
        m_SrvConfig[i].usId = (UShort)(i * LOADI_RADIX);
    }
}

INLINE CServerConfig::~CServerConfig(void)
{
}

INLINE CFilePath& CServerConfig::GetFilePath(void)
{
    return m_FilePath;
}

INLINE CKeyValue& CServerConfig::GetKeyValue(void)
{
    return m_KeyValue;
}

INLINE Int CServerConfig::GetNetworkAttr(void) const
{
    return m_nNetworkAttr;
}

INLINE Int CServerConfig::GetNetworkThread(void) const
{
    return m_nNetworkThread;
}

INLINE Int CServerConfig::GetNetworkAck(void) const
{
    return m_nNetworkAck;
}

INLINE Int CServerConfig::GetNetworkTimeout(void) const
{
    return m_nNetworkTimeout;
}

INLINE Int CServerConfig::GetNetworkBuffer(void) const
{
    return m_nNetworkBuffer;
}

INLINE Int CServerConfig::GetNetworkJumbo(void) const
{
    return m_nNetworkJumbo;
}

INLINE Int CServerConfig::GetFrameLogLevel(void) const
{
    return m_nFrameLogLevel;
}

INLINE Int CServerConfig::GetMgrLogLevel(void) const
{
    return m_nMgrLogLevel;
}

INLINE Int CServerConfig::GetUpdateTick(void) const
{
    return m_nUpdateTick;
}

INLINE Int CServerConfig::GetTimeoutStep(void) const
{
    if ((m_nNetworkAttr & CNETTraits::ATTR_TIMEOUT_TIME) != 0)
    {
        Int nStep = ((m_nNetworkTimeout / TIMET_XS2XS) - m_nUpdateTick) / m_nUpdateTick;
        if (nStep > 0)
        {
            return nStep;
        }
    }
    return -1;
}

INLINE Int CServerConfig::GetPingEnable(void) const
{
    return m_nPingEnable;
}

INLINE Int CServerConfig::GetAnyListen(void) const
{
    return m_nAnyListen;
}

INLINE Int CServerConfig::GetLoadServers(void) const
{
    return m_nLoadServers;
}

INLINE PCXStr CServerConfig::GetServerName(UInt uRank) const
{
    if (uRank < COMMONR_MAX)
    {
        return m_SrvConfig[uRank].strName.GetBuffer();
    }
    return nullptr;
}

INLINE UShort CServerConfig::GetServerType(UInt uRank) const
{
    UShort usType = COMMONT_NONE;
    if (uRank < COMMONR_MAX)
    {
        usType = m_SrvConfig[uRank].usType;
    }
    return usType;
}

INLINE UShort CServerConfig::GetServerSync(UInt uRank) const
{
    if (uRank < COMMONR_MAX)
    {
        return m_SrvConfig[uRank].usSync;
    }
    return COMMONR_MASTER;
}

INLINE UShort CServerConfig::GetServerId(UInt uRank) const
{
    UShort usRet = 0;
    if (uRank < COMMONR_MAX)
    {
        usRet = m_SrvConfig[uRank].usId;
    }
    else
    {
        assert(0);
    }
    return usRet;
}

INLINE UShort CServerConfig::GetServerQueue(UInt uRank) const
{
    UShort usRet = 0;
    if (uRank < COMMONR_MAX)
    {
        usRet = m_SrvConfig[uRank].usQueue;
    }
    else
    {
        assert(0);
    }
    return usRet;
}

INLINE UShort CServerConfig::GetServerLoad(UInt uRank) const
{
    UShort usRet = 0;
    if (uRank < COMMONR_MAX)
    {
        usRet = m_SrvConfig[uRank].usLoad;
    }
    else
    {
        assert(0);
    }
    return usRet;
}

INLINE UShort CServerConfig::GetServerRate(UInt uRank) const
{
    UShort usRet = 0;
    if (uRank < COMMONR_MAX)
    {
        usRet = m_SrvConfig[uRank].usRate;
    }
    else
    {
        assert(0);
    }
    return usRet;
}

INLINE bool CServerConfig::GetServerListen(UInt uRank, CStrAddr& strAddr, bool bAny2Local) const
{
    bool bRet = true;
    if (uRank < COMMONR_MAX)
    {
        strAddr = m_SrvConfig[uRank].Listen;
    }
    else
    {
        assert(0);
        bRet = false;
    }
    if (bAny2Local && ((strAddr.strIp == AddrAnyV4) || (strAddr.strIp == AddrAnyV6)))
    {
        strAddr.strIp = LocalHost;
    }
    return bRet;
}

INLINE bool CServerConfig::GetServerConnect(UInt uRank, CStrAddr& strAddr, UInt uConnectRank) const
{
    bool bRet = true;
    if ((uRank < COMMONR_MAX) && (uConnectRank < COMMONR_MAX))
    {
        bRet = (m_SrvConfig[uRank].Connect[uConnectRank].usAttr != 0);
        if (bRet)
        {
            strAddr = m_SrvConfig[uRank].Connect[uConnectRank];
        }
    }
    else
    {
        assert(0);
        bRet = false;
    }
    return bRet;
}

INLINE bool CServerConfig::GetServerRoutine(UInt uRank, CString& strRoutine, CString& strInst, CString& strModule) const
{
    bool bRet = true;
    if (uRank < COMMONR_MAX)
    {
        strRoutine = m_SrvConfig[uRank].strRoutine;
        strInst    = m_SrvConfig[uRank].strInst;
        strModule  = m_SrvConfig[uRank].strModule;
    }
    else
    {
        assert(0);
        bRet = false;
    }
    return bRet;
}

INLINE const SERVER_CONFIG& CServerConfig::GetServerConfig(UInt uRank) const
{
    assert(uRank < COMMONR_MAX);
    return m_SrvConfig[uRank];
}

INLINE PCXStr CServerConfig::GetFramePath(void) const
{
    return m_strFramePath.GetBuffer();
}

INLINE PCXStr CServerConfig::GetRoutinePath(void) const
{
    return m_strRoutinePath.GetBuffer();
}

INLINE PCXStr CServerConfig::GetLogPath(void) const
{
    return m_strLogPath.GetBuffer();
}

INLINE void CServerConfig::CheckModule(CString& strModule) const
{
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
    CString strTemp = CServerConfig::LibPrefix;
    strTemp += strModule;
#else
    CString strTemp = strModule;
#endif
    strTemp += CServerConfig::LibExt;
    strModule = strTemp;
}

#endif // __SERVER_CONFIG_H__
